import * as fs from 'fs-extra';
import { format, replace, replaceToUpperCase } from '../util/lang';
import { Swagger, Parameter, Data, Properties, Definition, Origin } from '../interface';

const type = {
  'integer': 'number',
  'long': 'number',
  'float': 'number',
  'double': 'number',
  'string': 'string',
  'byte': 'string',
  'binary': 'string',
  'date': 'string',
  'dateTime': 'string',
  'password': 'string',
  'boolean': 'boolean',
  'array': '[]',
}

const interfaces = [];

const setName = (item: Data, action: string) => {
  let { formatUrl } = item;

  formatUrl = formatUrl.split('?')[0];
  const strArr = formatUrl.split('/').map(item => {
    return item.replace(/{|}/g, '').toLocaleLowerCase().split(/_|-/).map(subItem => {
      return subItem.replace(/:/, '').replace(/^\S/, s => s.toUpperCase());
    });
  });
  const result = [].concat(...[...strArr].splice(2));
  return `${action.replace(/^\S/, s => s.toUpperCase())}${result.join('')}`;
}

const setParameters = (parameters: Parameter[] = [], swagger: Swagger) => {
  return parameters.map((item: Parameter) => {
    if (item.schema) {
      const { properties = [] } = swagger.definitions[replace(item.schema.$ref)]
      return setProperties(properties);
    }
    if (item.type === 'array') {
      if (item.items && item.items.$ref) {
        const ts = replaceToUpperCase(item.items.$ref);
        return `${item.name}${item.required ? '' : '?'}: ${ts}${type[item.type] || 'any'}`;
      }
      if (item.items) {
        return `${item.name}${item.required ? '' : '?'}: ${type[item.items.type]}${type[item.type] || 'any'}`;
      }
      return `${item.name}${item.required ? '' : '?'}: any${type[item.type] || 'any'}`;
    }
    return `${item.name}${item.required ? '' : '?'}: ${type[item.type] || 'any'}`;
  })
}

const setPaths = (item: Data, swagger: Swagger, action: string) => {
  const name = setName(item, action);
  if (!item.parameters) {
    return '';
  }
  const parameters = item.parameters.filter((item: Parameter) => item.in !== 'header');
  if (parameters.length === 0) {
    return '';
  }
  interfaces.push(name);
  return `export interface ${name} {
    ${setParameters(parameters, swagger)}
  }`
}

const setProperties = (properties: Properties) => {
  return Object.keys(properties).map(key => {
    const item = properties[key];
    switch (item.type) {
      case 'object':
        if (!item.$ref) {
          return `${key}: object`;
        }
        const ts = replaceToUpperCase(item.$ref);
        return `${key}: ${ts}`;
      case 'array':
        if (item.items.$ref) {
          const ts = replaceToUpperCase(item.items.$ref);
          return `${key}: ${ts}${type[item.type] || 'any'}`;
        }
        return `${key}: ${type[item.items.type]}${type[item.type] || 'any'}`;
      default:
        return `${key}: ${type[item.type] || 'any'}`;
    }
  })
}

const setDefintions = (item: Definition, key: string) => {
  // const name = key.split('.')[1].replace(/^\S/, s => s.toUpperCase());
  const name = key.replace(/^\S/, s => s.toUpperCase());
  if (item.type === 'array') {
    interfaces.push(name);
    return `export type ${name} = ${replace(item.items.$ref).split('.')[1]}${type[item.type]};`;
  }
  if (!item.properties) {
    return '';
  }
  interfaces.push(name);
  return `export interface ${name} {
    ${setProperties(item.properties)}
  }`
}

const setInterfaceContent = (swagger: Swagger) => {
  const { paths, definitions } = swagger
  let result = '';
  Object.keys(definitions).map(key => {
    const item = definitions[key];
    result += setDefintions(item, key);
  })
  Object.keys(paths).map(el => {
    Object.keys(paths[el]).map(key => {
      const item = paths[el][key];
      const action = Object.keys(paths[el]).length > 1 ? key : '';
      result += setPaths(item, swagger, action);
    });
  })
  return result;
}

const setTsContent = (swagger: Swagger, namespace: string) => {
  const result = `
    /* eslint-disable @typescript-eslint/no-empty-interface */
    /**
     * ax-snippets 生成的 ts
     */
    declare namespace ${namespace} {
      ${setInterfaceContent(swagger)}
    };
  `
  return {
    result,
  }
}

export function tsGenerate(swagger: Swagger, origin: Origin, outDir: string) {
  const { name } = origin;
  const { result } = setTsContent(swagger, name);
  return new Promise((resolve, reject) => {
    fs.outputFile(`${outDir}/typing/${name}.d.ts`, format(result), err => {
      if (err) {
        reject(err);
      }
      console.log(`Generate ${name}.d.ts success!`)
      resolve(interfaces);
    });
  })
};
